home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Libris Britannia 4
/
science library(b).zip
/
science library(b)
/
DDJMAG
/
DDJ9006.ZIP
/
STEVENS.LST
< prev
next >
Wrap
File List
|
1990-05-02
|
19KB
|
676 lines
_C PROGRAMMING COLUMN_
by Al Stevens
[LISTING ONE]
/* ---------- hyprtree.h --------- */
#define NODELEN 256 /* length of a node */
#define MAXLEVELS 10 /* tree levels */
#define MAXKEYLENGTH 25 /* maximum key length */
#define TRUE 1
#define FALSE !TRUE
#define KSPACE (NODELEN-sizeof(NODEHDR))
#define HYPERTREE "hyprtree.ndx"
/* ----- computes length of a key in a node -------- */
#define keylength(nd,kp) (strlen(kp) + 1 + \
((nd).nodehdr.isleaf?sizeof(KEYVALUE):sizeof(NODEPOINTER)))
/* ---------- node pointers and key values ---------- */
typedef long KEYVALUE;
typedef int NODEPOINTER;
typedef union {
KEYVALUE keyvalue;
NODEPOINTER nodepointer;
} KEYPOINTER;
/* ---------- header record for a hypertree ---------- */
typedef struct {
NODEPOINTER rootnode;
} TREEHDR;
/* ---- header structure for a hypertree node -------- */
typedef struct {
int isleaf; /* true if the node is a leaf */
NODEPOINTER parent; /* node number of parent */
KEYPOINTER lower; /* keys < this node (or value if leaf)*/
} NODEHDR;
/* --------- node record for a hypertree ---------- */
typedef struct {
NODEHDR nodehdr;
char keys[KSPACE];
} TREENODE;
/* ------------- prototypes --------------- */
void addkey(char *key, KEYVALUE keyvalue);
int findkey(char *key);
int firstkey(void);
int lastkey(void);
int nextkey(void);
int prevkey(void);
KEYVALUE current_keyvalue(void);
char *current_keystring(void);
[LISTING TWO]
/* ---------------- addkey.c -------------- */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "hyprtree.h"
static void addkeyentry(int level, char *key,
KEYPOINTER keypointer, NODEPOINTER lowernode);
static void nullnode(int level);
static int freespace(TREENODE node);
static void writenode(int level);
static void adopt(NODEPOINTER child, NODEPOINTER parent);
static TREENODE *nodes[MAXLEVELS];
static NODEPOINTER nodenbr[MAXLEVELS];
static NODEPOINTER nextnode;
static FILE *htree;
static TREEHDR th;
/*
* Add a key string value and its associated KEYVALUE to
* the hypertree.
*/
void addkey(char *key, KEYVALUE keyvalue)
{
KEYPOINTER keypointer;
if (key == NULL) {
/* ------ terminal key, complete the tree ------ */
if (htree != NULL) {
int level = 0;
/* -------- write the unwritten nodes -------- */
while (nodes[level] != NULL) {
writenode(level);
free(nodes[level]);
level++;
}
/* ------ update the header block ---------- */
th.rootnode = nodenbr[level-1];
fseek(htree, 0L, SEEK_SET);
fwrite(&th, sizeof(TREEHDR), 1, htree);
fclose(htree);
}
}
else {
keypointer.keyvalue = keyvalue;
if (strlen(key) <= MAXKEYLENGTH)
addkeyentry(0, key, keypointer, 0);
}
}
static void addkeyentry(int level, char *key,
KEYPOINTER keypointer, NODEPOINTER lowernode)
{
char *kp;
if (nodes[level] == NULL) {
/* -------- build a new node ---------- */
if (level == 0) {
/* ------ building a new tree ------ */
htree = fopen(HYPERTREE, "wb+");
/* ----- write a NULL header for now ----- */
fwrite(&th, sizeof(TREEHDR), 1, htree);
}
if ((nodes[level] = malloc(sizeof(TREENODE))) == NULL) {
fputs("\nOut of memory!", stderr);
exit(1);
}
nodes[level+1] = NULL;
nullnode(level);
/* ---- point to the node of the lower keys --- */
nodes[level]->nodehdr.lower.nodepointer = lowernode;
/* --- assign the next node number to this node --- */
nodenbr[level] = ++nextnode;
}
if (keylength(*nodes[level],key) >
freespace(*nodes[level])) {
/* -------- this node is full -------- */
KEYPOINTER keyp;
NODEPOINTER lowernode;
/* ---- write the node to the index file ---- */
writenode(level);
/* ---- remember the node nbr at this level ---- */
lowernode = nodenbr[level];
/* --- assign a new node number to this level --- */
nodenbr[level] = ++nextnode;
/* --- grow or add to parent with the current key --- */
memset(&keyp, 0, sizeof(KEYPOINTER));
keyp.nodepointer = nodenbr[level];
addkeyentry(level+1, key, keyp, lowernode);
/* - now set the node at this level to NULL values - */
nullnode(level);
nodes[level]->nodehdr.lower = keypointer;
}
else {
/* ------ insert the key into the node ------ */
kp = nodes[level]->keys;
/* ----- scan to the end of the node ----- */
while (*kp)
kp += keylength(*nodes[level], kp);
strcpy(kp, key);
/* ----- attach the key pointer to the key ----- */
kp += strlen(kp)+1;
if (level == 0)
*((KEYVALUE *)kp) = keypointer.keyvalue;
else
*((NODEPOINTER *)kp) = keypointer.nodepointer;
}
}
/* --------- build a null node ------------ */
static void nullnode(int level)
{
memset(nodes[level], 0, NODELEN);
nodes[level]->nodehdr.isleaf = level == 0;
}
/* ------ compute space remaining in a node ------- */
static int freespace(TREENODE node)
{
int sp = KSPACE;
char *kp = node.keys;
while (*kp) {
sp -= keylength(node,kp);
kp += keylength(node,kp);
}
return sp;
}
/* ------ write a completed node ------- */
static void writenode(int level)
{
long where = (nodenbr[level]-1)*NODELEN+sizeof(TREEHDR);
fseek(htree, where, SEEK_SET);
fwrite(nodes[level], NODELEN, 1, htree);
if (level) {
/* - this is a parent node, update its children - */
char *kp = nodes[level]->keys;
adopt(nodes[level]->nodehdr.lower.nodepointer,
nodenbr[level]);
while (*kp) {
adopt(*((NODEPOINTER *)(kp+strlen(kp)+1)),
nodenbr[level]);
kp += keylength(*nodes[level], kp);
}
}
}
/* ---- write a parent node number into a child node ---- */
static void adopt(NODEPOINTER child, NODEPOINTER parent)
{
NODEHDR nodehdr;
long here = ftell(htree);
long where = (child-1)*NODELEN+sizeof(TREEHDR);
fseek(htree, where, SEEK_SET);
fread(&nodehdr, sizeof(NODEHDR), 1, htree);
nodehdr.parent = parent;
fseek(htree, where, SEEK_SET);
fwrite(&nodehdr, sizeof(NODEHDR), 1, htree);
fseek(htree, here, SEEK_SET);
}
[LISTING THREE]
/* ----------- srchtree.c ------------ */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "hyprtree.h"
/* ---------- search packet ----------- */
typedef struct {
NODEPOINTER node;
char *ky;
} SEARCH;
FILE *htree;
static TREEHDR th;
static TREENODE node;
static SEARCH current;
static void readnode(NODEPOINTER nodepointer);
static void opentree(void);
/*
* Find a specified key in the tree.
* Return TRUE if found.
* Return FALSE if not found.
*/
int findkey(char *key)
{
opentree();
if (th.rootnode) {
SEARCH save = current;
readnode(th.rootnode);
while (TRUE) {
int cmp;
current.ky = node.keys;
while (*current.ky) {
cmp = stricmp(key, current.ky);
if (cmp < 0) {
save = current;
break;
}
if (cmp == 0)
return TRUE;
current.ky += keylength(node, current.ky);
}
if (!node.nodehdr.isleaf) {
if (current.ky == node.keys)
readnode(node.nodehdr.lower.nodepointer);
else
readnode(*((NODEPOINTER *)
(current.ky-sizeof(NODEPOINTER))));
}
else {
current = save;
readnode(current.node);
break;
}
}
}
return FALSE;
}
/*
* Find the first sequential key in the tree.
* Return TRUE if found.
* Return FALSE if not found (the tree is empty).
*/
int firstkey(void)
{
opentree();
if (th.rootnode) {
readnode(th.rootnode);
while (!node.nodehdr.isleaf)
readnode(node.nodehdr.lower.nodepointer);
current.ky = node.keys;
return TRUE;
}
return FALSE;
}
/*
* Find the last sequential key in the tree.
* Return TRUE if found.
* Return FALSE if not found (the tree is empty).
*/
int lastkey(void)
{
NODEPOINTER np;
opentree();
if (th.rootnode) {
int len = 0;
np = th.rootnode;
current.node = th.rootnode;
current.ky = node.keys;
do {
SEARCH save = current;
readnode(np);
current.ky = node.keys;
while (*current.ky) {
len = keylength(node, current.ky);
current.ky += len;
}
if (current.ky == node.keys) {
readnode(save.node);
current.ky = save.ky;
break;
}
else
np = *((NODEPOINTER *)
(current.ky-sizeof(NODEPOINTER)));
} while (!node.nodehdr.isleaf);
current.ky -= len;
return TRUE;
}
return FALSE;
}
/*
* Find the next sequential key in the tree.
* Return TRUE if found.
* Return FALSE if not found (the tree is empty or at the end).
*/
int nextkey(void)
{
opentree();
if (th.rootnode) {
if (current.ky == NULL)
return firstkey();
current.ky += keylength(node, current.ky);
if (!node.nodehdr.isleaf) {
readnode(*((NODEPOINTER *)
(current.ky-sizeof(NODEPOINTER))));
current.ky = node.keys;
while (!node.nodehdr.isleaf)
readnode(node.nodehdr.lower.nodepointer);
}
/* ----- while at the end of a node ------ */
while (*current.ky == '\0') {
NODEPOINTER child = current.node;
if (node.nodehdr.parent == 0)
break;
readnode(node.nodehdr.parent);
current.ky = node.keys;
if (child == node.nodehdr.lower.nodepointer)
break;
while (*current.ky) {
NODEPOINTER this = *((NODEPOINTER *)
(current.ky+strlen(current.ky)+1));
current.ky += keylength(node, current.ky);
if (this == child)
break;
}
}
return *current.ky != '\0';
}
return FALSE;
}
/*
* Find the previous sequential key in the tree.
* Return TRUE if found.
* Return FALSE if not found
* (the tree is empty or at the beginning).
*/
int prevkey(void)
{
char *kp;
opentree();
if (th.rootnode) {
if (current.ky == NULL)
return lastkey();
if (!node.nodehdr.isleaf) {
/* ----- navigate to end of the lower leaf ----- */
NODEPOINTER np;
if (current.ky == node.keys)
np = node.nodehdr.lower.nodepointer;
else
np = *((NODEPOINTER *)
(current.ky-sizeof(NODEPOINTER)));
while (!node.nodehdr.isleaf) {
readnode(np);
current.ky = node.keys;
while (*current.ky)
current.ky += keylength(node, current.ky);
np = *((NODEPOINTER *)
(current.ky-sizeof(NODEPOINTER)));
}
}
/* ----- while at the beginning of a node ------ */
while (current.ky == node.keys) {
NODEPOINTER child = current.node;
if (node.nodehdr.parent == 0)
break;
readnode(node.nodehdr.parent);
current.ky = node.keys;
if (child == node.nodehdr.lower.nodepointer)
continue;
while (*current.ky) {
NODEPOINTER this = *((NODEPOINTER *)
(current.ky+strlen(current.ky)+1));
current.ky += keylength(node, current.ky);
if (this == child)
break;
}
}
/* -------- go to previous key in node -------- */
if (current.ky != node.keys) {
kp = node.keys;
while (kp+keylength(node, kp) < current.ky)
kp += keylength(node, kp);
current.ky = kp;
return TRUE;
}
}
return FALSE;
}
/*
* Return the key value associated with the most recently
* retrieved key.
*/
KEYVALUE current_keyvalue(void)
{
KEYVALUE rtn;
if (!node.nodehdr.isleaf) {
SEARCH save = current;
current.ky += keylength(node, current.ky);
while (!node.nodehdr.isleaf) {
NODEPOINTER np;
if (current.ky == node.keys)
np = node.nodehdr.lower.nodepointer;
else
np = *((NODEPOINTER *)
(current.ky-sizeof(NODEPOINTER)));
readnode(np);
current.ky = node.keys;
}
rtn = node.nodehdr.lower.keyvalue;
current = save;
readnode(save.node);
return rtn;
}
return *((KEYVALUE *)(current.ky+strlen(current.ky)+1));
}
/*
* Return the key string value of the most recently
* retrieved key.
*/
char *current_keystring(void)
{
return current.ky;
}
/* -------- open the tree ----------- */
static void opentree(void)
{
if (htree == NULL) {
if ((htree = fopen(HYPERTREE, "rb")) == NULL) {
fputs("\n" HYPERTREE " not found", stderr);
exit(1);
}
fread(&th, sizeof(TREEHDR), 1, htree);
}
}
/* ---------- read a specified node ------------- */
static void readnode(NODEPOINTER nodepointer)
{
long where = (nodepointer-1)*NODELEN+sizeof(TREEHDR);
fseek(htree, where, SEEK_SET);
fread(&node, NODELEN, 1, htree);
current.node = nodepointer;
}
[LISTING FOUR]
/* -------- keyextr.c ---------- */
/*
* Build a test HyperTree from standard input.
* <> delimiters define the key values.
* This is pass one, which builds the sortable table.
*/
#include <stdio.h>
#include <string.h>
#include "hyprtree.h"
#define KEYTOKEN '<'
#define TERMINAL '>'
#define QUOTE '"'
#define ESCAPE '\\'
void main(void)
{
int c;
while ((c = getchar()) != EOF) {
if (c == KEYTOKEN) {
long fileposition = ftell(stdin)-1;
int counter = 0;
putchar(QUOTE);
while ((c = getchar()) != TERMINAL) {
if (c == EOF || counter++ == MAXKEYLENGTH)
break;
if (c == QUOTE)
putchar(ESCAPE);
putchar(c);
}
putchar(QUOTE);
putchar(',');
printf("%ld\n", fileposition);
}
}
}
[LISTING FIVE]
/* ----------- keybuild.c ----------- */
/*
* Build a test HyperTree from standard input.
* This is pass three, which builds the HyperTree
* from the sorted table.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "hyprtree.h"
#define QUOTE '"'
#define ESCAPE '\\'
void main(void)
{
int c;
char key[MAXKEYLENGTH+1];
char kv[20];
KEYVALUE keyvalue;
while ((c = getchar()) != EOF) {
if (c == QUOTE) {
int counter = 0;
char *cp = key;
while ((c = getchar()) != QUOTE) {
if (c == EOF || counter++ == MAXKEYLENGTH)
break;
if (c == ESCAPE)
c = getchar();
*cp++ = c;
}
*cp = '\0';
if (getchar() == ',') {
char *kp = kv;
while ((c = getchar()) != '\n' && c != EOF)
*kp++ = c;
*kp = '\0';
keyvalue = atol(kv);
addkey(key, keyvalue);
}
}
}
addkey(NULL, keyvalue);
}
[LISTING SIX]
/* ------------- hyprsrch.c -------------- */
/*
* Search the test HyperTree for a match on the
* command-line parameter.
* Display a screen of text starting at the
* position indicated for the matching (or closest)
* entry in the HyperTree.
*/
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <conio.h>
#include "hyprtree.h"
#define SCRLINES 25
void main(int argc, char *argv[])
{
if (argc < 3)
fprintf(stderr, "Usage: hyprsrch textfile keyvalue");
else {
int kb = 0;
FILE *fp = fopen(argv[1], "r");
if (fp == NULL) {
fprintf(stderr, "No such file as %s", argv[1]);
return;
}
findkey(argv[2]);
while (toupper(kb) != 'Q') {
int lc = 4, c;
KEYVALUE kv = current_keyvalue();
printf("\n%s (%ld)", current_keystring(), kv);
printf("\n------------------------------------\n");
if (kv) {
fseek(fp, kv, SEEK_SET);
while ((c = getc(fp)) != EOF && lc < SCRLINES){
if (c == '\n')
lc++;
putchar(c);
}
}
printf("\nF = first, "
"L = last, "
"N = next, "
"P = previous, "
"Q = quit...");
kb = getch();
switch (toupper(kb)) {
case 'F':
firstkey();
break;
case 'L':
lastkey();
break;
case 'N':
nextkey();
break;
case 'P':
prevkey();
break;
default:
break;
}
}
}
}